<?php
namespace app\admin\library;

use app\common\model\Area;
use think\Db;
use think\Log;

class DistinguishAddress
{

    /**
     * 类的入口方法
     * 传入地址信息自动识别，并返回最高匹配结果
     * 如果地址新增，则需要删除缓存文件重新缓存
     * @param $address
     * @param $deep
     * @return array|bool|mixed
     */
    function getAddressResult($address, $deep = true)
    {
        // 优先第一种方法
        $result = $this->getAddressArray($address);

        if ($deep) {
            // 如果结果不理想，再模糊去匹配
            if ($result['level'] != 3) {
                $result_sub = $this->addressVague($address);

                // 只有全匹配对才替换，否则不做任何改变
                if ($result_sub['level'] == 3) {
                    $result = $result_sub;
                }
            }
        }
        return $result;



//        // 联系方式-优先匹配电话
//        if (preg_match('/1\d{10}/', $address, $mobiles)) { // 手机
//            $result['mobile'] = $mobiles[0];
//        } else if (preg_match('/(\d{3,4}-)?\d{7,8}/', $address, $mobiles)) { // 固定电话
//            $result['mobile'] = $mobiles[0];
//        }
//        // 识别姓名-必须空格分享的--概率
//        preg_match_all('/[\x{4e00}-\x{9fa5}]{2,}/iu', $address, $names);
//        if ($names) {
//            $name_where = '';
//            foreach ($names[0] as $name) {
//                // 必须是大于1个字符且小于5个字符的
//                if (1 < mb_strlen($name, 'utf-8') && mb_strlen($name, 'utf-8') < 5) {
//                    $sub_name = mb_substr($name, 0, 1, 'utf-8');
//                    $name_where .= "name like '{$sub_name}%' or ";
//                }
//            }
//            if (!empty($name_where)) {
//                $name_where = substr($name_where, 0, -3);
//                $names_sql = "select name from surname where {$name_where} order by sort desc";
//                $list = Db::query($names_sql);
//
//                // 统计有多少种可能性-姓名
//                $result['name_num'] = count($list);
//
//                if ($list) {
//                    $name_first = $list[0]['name'];
//
//                    foreach ($names[0] as $name) {
//                        $len = mb_strlen($name_first, 'utf-8');
//                        if (mb_substr($name, 0, $len, 'utf-8') == $name_first) {
//                            $result['name'] = $name;
//                        }
//                    }
//                }
//            }
//        }
//        // 去掉详细里面的姓名和电话
//        $result['detail'] = str_replace($result['mobile'], '', $result['detail']);
//        $result['detail'] = str_replace($result['name'], '', $result['detail']);
//        $result['detail'] = $result['province']['name'] . $result['city']['name'] . $result['district']['name'] . $result['detail'];
//        return $this->getCityLevelList($result);


    }

    /**
     * 获取对应城市等级列表
     */
    function getCityLevelList($result)
    {
        // 获取所有地址递归列表
        $areas = $this->getAreaTreeList();
        // 获取省份列表- 只有存在值才返回对应列表
        $province_id = $result['province']['id'];
        if ($province_id) {
            foreach ($areas as $area) {
                unset($area['children']);
                $result['province_list'][] = $area;
            }
        }
        // 获取城市列表- 只有存在值才返回对应列表
        $city_id = $result['city']['id'];
        if ($city_id) {
            foreach ($areas[$province_id]['children'] as $area) {
                unset($area['children']);
                $result['city_list'][] = $area;
            }
        }
        // 获取地区列表- 只有存在值才返回对应列表

        $district_id = $result['district']['id'];
        if ($district_id) {
            foreach ($areas[$province_id]['children'][$city_id]['children'] as $area) {
                unset($area['children']);
                $result['district_list'][] = $area;
            }
        }

        return $result;
    }

    /**
     * 获取所有地址递归列表
     */
    function getAreaTreeList()
    {
        $file_name = 'areas.json';
        if (is_file($file_name)) {
            $areas = file_get_contents($file_name);
            $areas = json_decode($areas, true);
        } else {
            $area_sql = "select id,name,pid from fa_area";
            $areas = Db::query($area_sql);
            $areas = $this->arrayKey($areas);
            file_put_contents($file_name, json_encode($areas, true));
        }
        return $areas;
    }

    /**
     * 第一种方法
     * 根据地址列表递归查找准确地址
     * @param $address
     * @return array
     */
    function getAddressArray($address)
    {
        // 获取所有地址递归列表
        $areas = $this->getAreaTreeList();
        // 初始化数据
        $province = $city = $district = array();

        // 先查找省份-第一级地区
        $province = $this->checkAddress($address, $areas);
        if ($province) {
            // 查找城市-第二级地区
            $city = $this->checkAddress($address, $province['list']);
            if ($city) {
                // 查找地区-第三级地区
                // 西藏自治区那曲市色尼区辽宁南路西藏公路  第三个参数因为这个地址冲突取消强制
                $district = $this->checkAddress($address, $city['list']);
            }
        }

        //dd(compact('address', 'province', 'city', 'district'));

        return $this->getAddressInfo($address, $province, $city, $district);
    }

    /**
     * 第二种方法
     * 地址模糊查找
     * @param $address
     * @return array|bool
     */
    function addressVague($address)
    {
        $res = preg_match_all('/\S{2}[自市区镇县乡岛州]/iu', $address, $arr);
        if (!$res) return false;

        $where = ' where ';
        foreach ($arr[0] as $value) {
            if (strpos($value, '小区') === false && strpos($value, '开发区') === false) {
                $where .= "name like '%{$value}' or ";
            }
        }
        $where = substr($where, 0, -3);

        $area_sql = "select id,name,pid,level from fa_area " . $where;
        $citys = Db::query($area_sql);

        // 匹配所有地址
        $result = array();
        foreach ($citys as &$city) {
            // 所有相关联的地区id
            $city_ids = array();

            if ($city['level'] == 2) {
                $city_ids = array($city['pid'], $city['id']);

                // 尝试能不能匹配第三级
                $area_sql = "select id,name,pid,level,left(name,2) as ab_name from fa_area where pid='{$city['id']}'";
                $areas = Db::query($area_sql);
                foreach ($areas as $row) {
                    if (mb_strpos($address, $row['ab_name'])) {
                        $city_ids[] = $row['id'];
                    }
                }
            } else if ($city['level'] == 3) {
                $area = Area::where('id', $city['pid'])->field('pid')->find();
                $city['province_id'] = @$area['pid'];
                $city_ids = array($city['pid'], $city['id'], $city['province_id']);
            }

            if ($city_ids) {
                // 查找该单词所有相关的地区记录
                $where = " where id in(" . join(',', $city_ids) . ")";

                $area_sql = "select id,name,pid,level,left(name,2) as ab_name from fa_area " . $where . ' order by id asc';
                $city_list = Db::query($area_sql);

                sort($city_ids);
                $key = array_pop($city_ids);
                $result[$key] = $city_list;
                sort($result);
            }

        }

        if ($result) {

            $result = $result[0];
            $province = $city = $district = null;
            if (isset($result[0])) {
                $province = $result[0];
            }
            if (isset($result[1])) {
                $city = $result[1];
            }
            if (isset($result[2])) {
                $district = $result[2];
            }

            //list($province, $city, $area) = $result[0];
            return $this->getAddressInfo($address, $province, $city, $district);
        }

        return false;
    }

    /**
     * 匹配正确的城市地址
     * @param $address
     * @param $city_list
     * @param bool $force
     * @param int $str_len
     * @return array
     */
    function checkAddress($address, $city_list, $force = false, $str_len = 2)
    {
        $num = 0;
        $list = array();
        $result = array();

        // 遍历所有可能存在的城市
        foreach ($city_list as $city_key => $city) {
            $city_name = mb_substr($city['name'], 0, $str_len, 'utf-8');

            // 判断是否存包含当前地址字符
            $city_arr = explode($city_name, $address);

            // 如果存在相关字眼，保存该地址的所有子地址
            if (count($city_arr) >= 2) {

                // 必须名称长度同时达到当前比对长度
                if (strlen($city['name']) < $str_len) {
                    continue;
                }

                $num++;
                $list = $list + $city['children'];

                $result[] = array(
                    'id' => $city['id'],
                    'name' => $city['name'],
                    'list' => $list,
                );
            }
        }

        // 如果有多个存在，则加大字符匹配长度
        if ($num > 1 || $force) {
            $name1 = $result[0]['name'];
            $name2 = $result[1]['name'];

            if (strlen($name1) == strlen($name2) && strlen($name1) == $str_len) {
                $id1 = $result[0]['id'];
                $id2 = $result[1]['id'];
                $index = $id1 > $id2 ? 1 : 0;
                $result = $result[$index];
                return $result;
            }
            return $this->checkAddress($address, $city_list, $force, $str_len + 1);
        } else {
            $result[0]['list'] = $list;
            return $result[0];
        }
    }

    /**
     * 根据原地址返回详细信息
     * @param $address
     * @param $province
     * @param $city
     * @param $district
     * @return array
     */
    function getAddressInfo($address, $province, $city, $district)
    {
        // 查找最后出现的地址 - 截取详细信息
        $find_str = '';
        if (isset($province['name']) && $province['name']) {
            $find_str = $province['name'];
            if (isset($city['name']) && $city['name']) {
                $find_str = $city['name'];
                if (isset($district['name']) && $district['name']) {
                    $find_str = $district['name'];
                }
            }
        }

        // 截取详细的信息
        $find_str_len = mb_strlen($find_str, 'utf-8');
        for ($i = 0; $i < $find_str_len - 1; $i++) {
            $substr = mb_substr($find_str, 0, $find_str_len - $i, 'utf-8');
            $end_index = mb_strpos($address, $substr);
            if ($end_index) {
                $address = mb_substr($address, $end_index + mb_strlen($substr), mb_strlen($address) - $end_index);
            }
        }
        !empty($find_str) && $find_str = '|\S*' . $find_str;
        $area['detail'] = preg_replace("/\s*|,|，|:|：{$find_str}/i", '', $address);

        $level = 0;
        if (isset($district['name']) && $district['name']) {
            $level = 3;
        } else if (isset($city['name']) && $city['name']) {
            $level = 2;
        } else if (isset($province['name']) && $province['name']) {
            $level = 1;
        }

        return array(
            'province' => array('id' => isset($province['id']) && $province['id'] ? $province['id'] : 0, 'name' => isset($province['name']) && $province['name'] ? $province['name'] : ''),
            'city' => array('id' => isset($city['id']) && $city['id'] ? $city['id'] : 0, 'name' => isset($city['name']) && $city['name'] ? $city['name'] : ''),
            'district' => array('id' => isset($district['id']) && $district['id'] ? $district['id'] : 0, 'name' => isset($district['name']) && $district['name'] ? $district['name'] : ''),
            'detail' => $area['detail'],
            'level' => $level,
        );
    }

    /**
     * 递归所有地址成无限分类数组
     * @param $data
     * @param int $id
     * @return array
     */
    function arrayKey($data, $id = 0)
    {
        $result = array();
        foreach ($data as $row) {
            if ($id == $row['pid']) {
                $key = $row['id'];
                $row['children'] = $this->arrayKey($data, $row['id']);
                $result[$key] = $row;
            }
        }
        return $result;
    }
}

?>
